package mymyoux.io.mserverconnectorClasses
{
	import mymyoux.core.IObject;
	import mymyoux.events.ErrorEvent;
	import mymyoux.events.MAdvSocketEvent;
	import mymyoux.events.MEventDispatcher;
	import mymyoux.events.MPlaylistEvent;
	import mymyoux.utils.Hash;
	import mymyoux.utils.StringUtils;
	 	 	 /**
         * MPlaylist dispatche l'event people_in
         * quand un utilisateur se connecte
         * @eventType mymyoux.events.MRoomEvent
         */
        [Event(name="people_in", type="mymyoux.events.MPlaylistEvent")]
        	 /**
         * MPlaylist dispatche l'event people_out
         * quand un utilisateur se déconnecte
         * @eventType mymyoux.events.MRoomEvent
         */
        [Event(name="people_out", type="mymyoux.events.MPlaylistEvent")]
         /**
         * MPlaylist dispatche l'event people_change_infos
         * quand un utilisateur met à jour ses informations
         * @eventType mymyoux.events.MRoomEvent
         */
        [Event(name="people_change_infos", type="mymyoux.events.MPlaylistEvent")]
        	/**
	 * MPlaylist permet la gestion des personnes, la liste des salons dans lesquelles elles se trouvent, leurs informations...
	 * @author Mymyoux
	 * @version 2.0
	 */
	public class MPlaylist extends MEventDispatcher implements IObject
	{
		/**
		 * Tableau contenant tous les MAdvSocket. Permet ainsi à MPlaylist de leur associer un id à chacun.
		 */
		protected var sockets:Array;
		/**
		 * Tableau contenant tous les salons ouverts. Permet ainsi à MPlaylist de leur associer un id à chacun.
		 */
		protected var salons:Array;
		/**
		 * Tableau associant à chaque id de salon un id de MadvSocket.
		 */
		protected var salonSocks:Array;
		/**
		 * Tableau contenant tous les MServerIdentifier des joueurs connectés.
		 * Contient également celui du joueur en cours.
		 */
		protected var joueurs:Hash;
		/**
		 * salonJoueurs[idsalon] est un tableau contenant tous les id des joueurs présents dans le salon associé à idsalon.
		 */
		protected var salonJoueurs:Array;
		/**
		 * joueurSalons[idsocket] est un Hash associant à chaque id de joueur du MadvSocket, le tableau des noms de salons dans lequel il se trouve.
		 */
		protected var joueurSalons:Array;
		/**
		 * socksJoueurs[idsalon] est un Hash associant à chaque id de joueur du MadvSocket, son idx ou 0 s'il ne s'est pas encore identifié.
		 */
		protected var socksJoueurs:Array;
		/**
		 * @private
		*/
		protected var _me:MServerIdentifier;
		/**
		 * Crée un objet de type MPlaylist.
		 * @param me MServerIdentifier représentant le joueur en cours.
		 */
		public function MPlaylist(me:MServerIdentifier)
		{
			_me=me;
			sockets=new Array();
			salons=new Array();
			salonSocks=new Array();
			joueurs=new Hash();
			salonJoueurs=new Array();
			joueurSalons=new Array();
			socksJoueurs=new Array();
		}
		/**
		 *Servant spécialement aux MRoomPlaylist pour obtenir un joueur grâce à son id.
		 *@param id Id du joueur.
		 *@param name Nom de la MRoom dans laquelle se trouve le joueur.
		 *@return Le MServerIdentifier du joueur ou un vide(ie idx=0) s'il n'exite pas.
		 */
		public function getPeople(id:int,name:String):MServerIdentifier{
			if(name!=null){
				if(id>0 && name.length>0){
					var idsal:int=salons.indexOf(name);
					if(idsal>=0){
						if(salonSocks.length>idsal){
							var idsocket:int=salonSocks[idsal];
							if(idsocket>=0){
								if(socksJoueurs.length>idsocket){
									if(socksJoueurs[idsocket].containsKey(id)){
										var idx:int=StringUtils.toNumber(socksJoueurs[idsocket].get(id)) as int;
										if(idx>0){
											if(joueurs.containsKey(idx)){
												return joueurs.get(idx) as MServerIdentifier;
											}
										}
									}
								}
							}
						}
					}
				}
			}
			return new MServerIdentifier(0);
		}
		/**
		 * Servant spécialement aux MRoomPlaylist pour obtenir l'id d'un joueur.
		 *@param people MServerIdentifier correspondant au joueur.
		 *@param name Nom de la MRoom où se trouve le joueur.
		 *@return L'id du joueur ou 0 si le joueur n'existe pas.
		 */
		public function getId(people:MServerIdentifier,name:String):uint{
			if(people!=null){
				if(people.idx>0){
					if(name!=null){
						if(name.length>0){
							var idsal:int=salons.indexOf(name);
							if(idsal>=0){
								if(salonSocks.length>idsal){
									var idsocket:int=salonSocks[idsal];
									if(idsocket>=0){
										if(socksJoueurs.length>idsocket){
											if(socksJoueurs[idsocket].containsValue(people.idx)){
												var ids:Array=socksJoueurs[idsocket].getKeys(people.idx);
												if(ids.length==1){
													if(ids[0]>0){
														return ids[0];
													}												
												}
											}										
										}
									}		
								}
							}
						}				
					}				
				}			
			}
			return 0;
		}
		/**
		 * Renvoie l'id associé au MAdvSocket passé en paramètre. Renvoie -1 si le socket n'existe pas.
		 * @param socket MadvSocket dont on souhaite obtenir l'id
		 * @return id du MAdvSocket.
		 */
		protected function getSocketId(socket:MAdvSocket):int{
			for(var i:uint=0;i<sockets.length;i++){
				if(sockets[i].equals(socket)){
					return i;
				}
			
			}
			return -1;
		}
		/**
		 * Ajoute un MAdvSocket a écouté par la MPlaylist.
		 * @param socket Le MAdvSocket.
		 */
		public function addServer(socket:MAdvSocket):void{
			if(getSocketId(socket)<0){
				sockets.push(socket);
				socksJoueurs.push(new Hash());
				joueurSalons.push(new Hash());
				socket.addEventListener(MAdvSocketEvent.IDENTIFIED,onIdentified);
				socket.addEventListener(MAdvSocketEvent.NEW_CONNECTED,onPlayersConnect);
				socket.addEventListener(MAdvSocketEvent.LOST_CONNECTED,onPlayerDisconnect);
				socket.addEventListener(MAdvSocketEvent.LIST,onPlayersConnect);
				socket.addEventListener(MAdvSocketEvent.GET_INFO,onPlayerInfo);
				socket.addEventListener(MAdvSocketEvent.ROOM_CLOSED,onRoomClosed);
				socket.addEventListener(MAdvSocketEvent.CLOSE,onSocketClose);
			}
		}
		/**
		 * Supprime un MAdvSocket. Stoppe son écoute.
		 * @param socket Le MAdvSocket.
		 */
		public function remServer(socket:MAdvSocket):void{
			var idsocket:int=getSocketId(socket);
			if(idsocket>=0){
				socket.dispatchEvent(new MAdvSocketEvent(MAdvSocketEvent.CLOSE,null));
				socket.removeEventListener(MAdvSocketEvent.IDENTIFIED,onIdentified);
				socket.removeEventListener(MAdvSocketEvent.NEW_CONNECTED,onPlayersConnect);
				socket.removeEventListener(MAdvSocketEvent.LOST_CONNECTED,onPlayerDisconnect);
				socket.removeEventListener(MAdvSocketEvent.LIST,onPlayersConnect);
				socket.removeEventListener(MAdvSocketEvent.GET_INFO,onPlayerInfo);
				socket.removeEventListener(MAdvSocketEvent.ROOM_CLOSED,onRoomClosed);
				sockets.splice(idsocket,1);
				socksJoueurs.splice(idsocket,1);
				joueurSalons.splice(idsocket,1);
			}
		}
		/**
		 * Appelée lors qu'un joueur envoie une mise à jour de son profil.
		 * @param MAdvSocketEvent Contient les informations nécessaires à cette mise à jour.
		 */
		 //protected funtion createPlayer(
		protected function onPlayerInfo(e:MAdvSocketEvent):void{
			var socket:MAdvSocket=e.target as MAdvSocket;
			var idsocket:int=getSocketId(socket);
			if(idsocket>=0){
				try{
					var people:MServerIdentifier=MServerIdentifier.unserialize(e.value);
					var people2:MServerIdentifier;
					var rooms:Array;
					var i:int=0;
					
					//premieres infos
					if(!joueurs.containsKey(people.idx)){
						joueurs.put(people.idx,people);
						dispatchEvent(new MPlaylistEvent(MPlaylistEvent.PEOPLE_IN,people));
						//déjà des infos
					}else{
						people2=joueurs.get(people.idx)as MServerIdentifier;
						people2.fusion(people);
						people=people2;
					}
					//association idx <=>id si elle n'est pas déjà faite et dispatch de l' event de connexion du coup
					if(!socksJoueurs[idsocket].containsKey(e.id) || StringUtils.toNumber(socksJoueurs[idsocket].get(e.id))==0){
						socksJoueurs[idsocket].put(e.id,people.idx);
						//envoi de levenement de connexion dans les aalons
						rooms=joueurSalons[idsocket].get(e.id);
						for(;i<rooms.length;i++){
							socket.dispatchEvent(new MAdvSocketEvent(MAdvSocketEvent.NEW_CONNECTED,rooms[i]+":"+e.id,e.id));
						}
					}else{
						//seul évènement à n'être dispatché que pour la globalité pas pour un salon uniquement
						dispatchEvent(new MPlaylistEvent(MPlaylistEvent.PEOPLE_CHANGE_INFO,people));
					}
				}catch(e:ErrorEvent){
				
				}
			}
		}
		/**
		 * Appelée lorsqu'un MAdvSocket est fermé.
		 * @param MAdvSocketEvent Evenement envoyé par le MadvSocketEvent.
		 */
		protected function onSocketClose(e:MAdvSocketEvent):void{
			var socket:MAdvSocket=e.target as MAdvSocket;
			var idsocket:int=getSocketId(socket);
			if(idsocket>=0){
				var i:int;
				var listeSocks:Array=salonSocks.join(",").split(",");//fait une copie profonde de la liste des idsock par salon
				var listeSalons:Array=salons.join(",").split(",");
				for(i=0;i<listeSocks.length;i++){
					if(listeSocks[i]==idsocket){
						if(listeSalons.length>i){
							socket.dispatchEvent(new MAdvSocketEvent(MAdvSocketEvent.ROOM_CLOSED,listeSalons[i]));
						}
					}
				}
			}else{
				socket.removeEventListener(MAdvSocketEvent.CLOSE,onSocketClose);
			}
		}
		/**
		 * Appelée lorsque le joueur quitte un salon. Permet de mettre à jour les playlists.
		 * @param MAdvSocketEvent Evenement envoyé par le MadvSocketEvent.
		 */
		protected function onRoomClosed(e:MAdvSocketEvent):void{
			var salon:String=e.data;
			var idsal:int=salons.indexOf(salon);
			var liste:Array;
			var i:int;
			if(idsal>=0){
				liste=salonJoueurs[idsal];
				for(i=0;i<liste.length;i++){
					(e.target as MAdvSocket).dispatchEvent(new MAdvSocketEvent(MAdvSocketEvent.LOST_CONNECTED,salon+":"+liste[i],liste[i]));
				}
				salonSocks.splice(idsal,1);
				salonJoueurs.splice(idsal,1);
				salons.splice(idsal,1);
			}
		}
		/**
		 * Appelée lorsqu'un ou plusieurs joueurs se connectent dans un salon. Met à jour la liste des salons pour chacun 
		 * et si nécessaire leur demande une mise à jour de leur profil.
		 * @param MAdvSocketEvent Evenement envoyé par le MadvSocketEvent.
		 */
		protected function onPlayersConnect(e:MAdvSocketEvent):void{
			//e.value de la forme : salon:id1,id2,id3...
			if(e.value.indexOf(":")>-1){
				
				var salon:String=e.value.substring(0,e.value.indexOf(":"));
				var liste:Array=e.value.substring(e.value.indexOf(":")+1).split(",");
				var socket:MAdvSocket=e.target as MAdvSocket;
				var idsocket:int=getSocketId(socket);
				if(idsocket>=0){
					var ask:Array=new Array();
					var idsal:int;
					var rooms:Array;
					var player:MServerIdentifier;
					for(var i:String in liste){
						idsal=salons.indexOf(salon);
						//création du salon s'il n'existe pas
						if(idsal==-1){
							salons.push(salon);
							idsal=salons.length-1;
							salonSocks.push(idsocket);
							salonJoueurs.push(new Array());						
						}
						if(!socksJoueurs[idsocket].containsKey(liste[i])){
							socksJoueurs[idsocket].put(liste[i],0);
							ask.push(liste[i]);
						}
						if(!joueurSalons[idsocket].containsKey(liste[i])){
							joueurSalons[idsocket].put(liste[i],new Array());
						}
						
						var idx:int=socksJoueurs[idsocket].get(liste[i]);
						rooms=joueurSalons[idsocket].get(liste[i]) as Array;
						if(rooms.indexOf(salon)<0){
							rooms.push(salon);
						}
						if(salonJoueurs[idsal].indexOf(liste[i])<0){
							salonJoueurs[idsal].push(liste[i]);
						}
							if(idx>0){
								player=joueurs.get(idx) as MServerIdentifier;
								dispatchEvent(new MPlaylistEvent(salon+MPlaylistEvent.PEOPLE_IN,player));
								//impossible ?
								/*
								if(rooms.length==1){
									dispatchEvent(new MPlaylistEvent(MPlaylistEvent.PEOPLE_IN,player));
								}*/
							}
					}
					if(ask.length>0){
						socket.send("c"+ask);
					}
				}
			}
		}
		/**
		 * Appelée lorsqu'un joueur quitte un salon. Met à jour son profil.
		 * @param MAdvSocketEvent Evenement envoyé par le MadvSocketEvent.
		 */
		protected function onPlayerDisconnect(e:MAdvSocketEvent):void{
			if(e.value.indexOf(":")>-1){
				var salon:String=e.value.substring(0,e.value.indexOf(":"));
				var idsocket:int=getSocketId(e.target as MAdvSocket);
				if(idsocket>=0){
					var idsal:int=salons.indexOf(salon);
					var liste:Array;
					var idx:int;
					var player:MServerIdentifier;
	
					if(idsal>=0){
						liste=salonJoueurs[idsal];
						if(liste.indexOf(e.id)>=0){
							liste.splice(liste.indexOf(e.id),1);
							
						}
						liste=joueurSalons[idsocket].get(e.id);
						if(liste.indexOf(salon)>=0){
							liste.splice(liste.indexOf(salon),1);
						}
						if(socksJoueurs[idsocket].containsKey(e.id)){
							idx=socksJoueurs[idsocket].get(e.id);
							if(idx>0){
								player=joueurs.get(idx) as MServerIdentifier;
								if(player!=null){
									dispatchEvent(new MPlaylistEvent(salon+MPlaylistEvent.PEOPLE_OUT,player));
								}
							}
						}
						checkPlayer(idsocket,e.id);
					}
				}
			}
		}
		/**
		 * Vérifie si le joueur lié à l'id sur le MadvSocket n°idsocket est encore présent dans au moins un salon.
		 * Sinon le supprime totalement de la MPlaylist ou le désassocie d'un MAdvSocket.
		 * @param idsocket Id du MadvSocket auquel est lié l'id du joueur.
		 * @param id Id du joueur.
		 */
		protected function checkPlayer(idsocket:int,id:int):void{
			//check s'il faut ou non supprimer le joueur
			var idx:int=0;
			var liste:Array;
			var i:int;
			var id:int;
			var player:MServerIdentifier;
			if(socksJoueurs[idsocket].containsKey(id)){
				idx=socksJoueurs[idsocket].get(id) as int;
			}
			if(joueurSalons[idsocket].containsKey(id)){
				liste=joueurSalons[idsocket].get(id);
				//on supprime le joueur du socket car il n'est plus connecté à aucun salon en commun avec nous
				if(liste.length==0){
					joueurSalons[idsocket].remove(id);
					//on vire le lien idx <=> id du socket
					if(idx>0){
						if(socksJoueurs[idsocket].containsKey(id)){
							socksJoueurs[idsocket].remove(id);
						}
					}
				}
			}
			//si c'est un joueur déjà identifié alors on vérifie s'il est encore dans d'autres salons 
			//quelque soit le socket... sinon on le supprime complètement
			if(idx>0){
				for(i=0;i<sockets.length;i++){
					if(socksJoueurs[i].containsValue(idx)){
						//il reste au moins un salon où est présent le joueur
						return;	
					}
				}
				//le joueur n'existe plus on le supprime
				if(joueurs.getKeys(idx)){
					player=joueurs.get(idx) as MServerIdentifier;
					if(!player.equals(_me)){ //on ne peut pas être soit meme supprimé
						joueurs.remove(idx);
						if(player!=null){
							dispatchEvent(new MPlaylistEvent(MPlaylistEvent.PEOPLE_OUT,player));
						}
					}
				}
			}
		}
		/**
		 * Appelée lorsque le joueur reçoit son id de connection
		 * @param MAdvSocketEvent Evenement envoyé par le MadvSocketEvent.
		 */
		protected function onIdentified(e:MAdvSocketEvent):void{
			//(e.target as MAdvSocket).dispatchEvent(new MAdvSocketEvent(MAdvSocketEvent.GET_INFO,_me.serialize(),StringUtils.toNumber(e.value) as int));
		}
		/**
		 * Renvoie la liste des personnes déjà connectées.
		 * @param name Nom du salon.
		 * @return Array de MServerIdentifier.
		 */
		 public function getConnecteds(name:String):Array{
		 	var list:Array=new Array();
		 	var idsal:int=salons.indexOf(name);
		 	var listId:Array;
		 	var idx:int;
		 	var player:MServerIdentifier;
		 	if(idsal>=0){
		 		if(salonSocks.length>idsal){
				var idsocket:int=salonSocks[idsal];
			 		listId=salonJoueurs[idsal];
			 		for(var i:String in listId){
			 			if(socksJoueurs[idsocket].containsKey(listId[i])){
			 				idx=socksJoueurs[idsocket].get(listId[i]);
							if(idx>0){
								player=joueurs.get(idx) as MServerIdentifier;
								if(player!=null){
									list.push(player);
								}
							}
			 			}
			 		}
		 		}
		 	}
		 	return list;
		 }
		/**
		 * MServerIdentifier du joueur.
		 */
		public function get me():MServerIdentifier{
			return _me;
		}
		/**
		 * @inheritDoc 
		 */
		public override function destroy():void{
			var i:int;
			for(i=0;i<sockets.length;i++){
				sockets[i].removeEventListener(MAdvSocketEvent.IDENTIFIED,onIdentified);
				sockets[i].removeEventListener(MAdvSocketEvent.NEW_CONNECTED,onPlayersConnect);
				sockets[i].removeEventListener(MAdvSocketEvent.LOST_CONNECTED,onPlayerDisconnect);
				sockets[i].removeEventListener(MAdvSocketEvent.LIST,onPlayersConnect);
				sockets[i].removeEventListener(MAdvSocketEvent.GET_INFO,onPlayerInfo);
				sockets[i].removeEventListener(MAdvSocketEvent.ROOM_CLOSED,onRoomClosed);
				sockets[i].removeEventListener(MAdvSocketEvent.CLOSE,onSocketClose);
			}
			joueurs.clear();
			_me=new MServerIdentifier(0);
			sockets=new Array();
			salons=new Array();
			salonSocks=new Array();
			joueurs=new Hash();
			salonJoueurs=new Array();
			joueurSalons=new Array();
			socksJoueurs=new Array();
		}
		/**
		 * @inheritDoc 
		 */
		public override function equals(objet:IObject):Boolean{
			return this===objet;
		}
		/**
		 * @inheritDoc 
		 */
		public override function toString():String{
			return "[MPlaylist MAdvSockets=\""+sockets.length+"\" Salons=\""+salons.length+"\" Joueurs=\""+joueurs.length+"\"]";
		}
	}
}
